/*
 * Linux内核诊断工具--内核态入口
 *
 * Copyright (C) 2020 chushi Ltd.
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <linux/syscalls.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timex.h>
#include <linux/tracepoint.h>
#include <trace/events/irq.h>
#include <trace/events/napi.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysctl.h>
#include <linux/semaphore.h>
#include <linux/vmalloc.h>
#include <trace/events/syscalls.h>

/**
 * x86 or arm64
 */
#if 0
#define ORIG_PARAM1(__regs) ((__regs)->regs[0])
#define ORIG_PARAM2(__regs) ((__regs)->regs[1])
#define ORIG_PARAM3(__regs) ((__regs)->regs[2])
#define ORIG_PARAM4(__regs) ((__regs)->regs[3])
#define ORIG_PARAM5(__regs) ((__regs)->regs[4])
#define ORIG_PARAM6(__regs) ((__regs)->regs[5])
#define RETURN_REG(__regs) ((__regs)->regs[0])
#else
#define ORIG_PARAM1(regs) ((regs)->di)
#define ORIG_PARAM2(regs) ((regs)->si)
#define ORIG_PARAM3(regs) ((regs)->dx)
#define ORIG_PARAM4(regs) ((regs)->cx)
#define ORIG_PARAM5(regs) ((regs)->r8)
#define ORIG_PARAM6(regs) ((regs)->r9)
#define RETURN_REG(__regs) ((__regs)->ax)
#endif

struct tp_reg {
    const char *name;
    struct tracepoint *tp;
    void *fn;
};

static void each_tracepoint(struct tracepoint *tp, void *priv)
{
    struct tp_reg *regs = (struct tp_reg *)priv;
    int i;
    
    for (i = 0; regs[i].name; ++i) {
        if (strcmp(tp->name, regs[i].name) == 0) {
            if (tracepoint_probe_register(tp, regs[i].fn, NULL) == 0) {
                regs[i].tp = tp;
            }
        }
    }
}

static void unregister_tp(struct tp_reg *regs)
{
    int i;

    for (i = 0; regs[i].name; ++i) {
        if (regs[i].tp) {
            tracepoint_probe_unregister(regs[i].tp, regs[i].fn, NULL);
            regs[i].tp = NULL;
        }
    }
    
    tracepoint_synchronize_unregister();
}

static int register_tp(struct tp_reg *regs)
{
    int i;
    
    for_each_kernel_tracepoint(each_tracepoint, regs);

    for (i = 0; regs[i].name; ++i) {
        if (regs[i].tp == NULL) {
            printk(KERN_ALERT "trace point %s not found.\n", regs[i].name);
            unregister_tp(regs);
            return 1;
        }
    }
    
    return 0;
}
#define __NR_dbg 1203
static void diag_cb_sys_enter(void *data, struct pt_regs *regs, long id)
{
	int ret = -ENOSYS;

	if (id == __NR_dbg) {
		size_t __user user_buf_len;
		void __user *user_buf;
		char *kbuf = NULL;

		user_buf = (void __user *)ORIG_PARAM1(regs);
		user_buf_len = (size_t)ORIG_PARAM2(regs);
        
		if (user_buf_len > 1024)
			user_buf_len = 1024;

		kbuf = kmalloc(user_buf_len + 1, GFP_KERNEL);
		if (!kbuf)
			ret = -ENOMEM;
		else {
			ret = copy_from_user(kbuf, user_buf, user_buf_len);
			if (ret)
				ret = -EFAULT;
			else
				printk("%s", kbuf);
				
			kfree(kbuf);
		}
		
		RETURN_REG(regs) = ret;
	}
}

static void trace_sys_enter_hit(void *__data, struct pt_regs *regs, long id)
{
	diag_cb_sys_enter(NULL, regs, id);
}

static struct tp_reg myreg[] = {
    {
    	.name = "sys_enter",
    	.fn = (void *)trace_sys_enter_hit,
    },
    {
    	.name = NULL,
    }
};

static int __init debug_printf_init(void)
{
	register_tp(myreg);
	
	return 0;
}

static void __exit debug_printf_exit(void)
{
	unregister_tp(myreg);
}

module_init(debug_printf_init);
module_exit(debug_printf_exit);

MODULE_DESCRIPTION("chushi debug-printf module");
MODULE_AUTHOR("Baoyou Xie <xiebaoyou@kernelsoft.com>");
MODULE_LICENSE("GPL v2");

